#include <algorithm>
#include <limits>
#include "Parser.h"
#include "ParserValidation.h"

std::map<std::string, std::string> contents;
std::string fName;

bool Parser::isNumber(std::string const& s) const {
    std::istringstream ss(s);
    double d;
    return (ss >> d) && (ss >> std::ws).eof();
}

bool Parser::validateRange(int const& typeValue, std::string const& key, std::string const& rangeFrom, std::string const& rangeTo) const
{
    bool valid = false;
    std::string rangeFromAux = rangeFrom;
    std::string rangeToAux = rangeTo;

    if(rangeFrom.find(':') != rangeFrom.npos){
        int varStart = rangeFrom.find_first_of(':') + 1;
        int varEnd = rangeFrom.find_first_of('-');
        rangeFromAux = Parser::getValueOfKey<std::string>(rangeFrom.substr(varStart, varEnd - varStart));
    }

    if(rangeTo.find(':') != rangeTo.npos){
        int varStart = rangeTo.find_first_of(':') + 1;
        int varEnd = rangeTo.find_first_of(')');
        rangeToAux = Parser::getValueOfKey<std::string>(rangeTo.substr(varStart, varEnd - varStart));
    }

    if(typeValue == INTEGER){
        int valueKey = Parser::getValueOfKey<int>(key);
        int intRangeFrom = string_to_T<int>(rangeFromAux);
        int intRangeTo = std::numeric_limits<int>::max();
        if(rangeToAux.empty() == 0){
            intRangeTo = string_to_T<int>(rangeToAux);
        }
        valid = (intRangeFrom <= valueKey) && (valueKey <= intRangeTo);
    } else {
        float valueKey = Parser::getValueOfKey<float>(key);
        float intRangeFrom = string_to_T<float>(rangeFromAux);
        float intRangeTo = std::numeric_limits<float>::max();
        if(!rangeToAux.empty()){
            intRangeTo = string_to_T<float>(rangeToAux);
        }
        valid = (intRangeFrom <= valueKey) && (valueKey <= intRangeTo);
    }

    return valid;
}

void Parser::removeComment(std::string &line) const
{
    if (line.find(';') != line.npos)
        line.erase(line.find(';'));
}

bool Parser::onlyWhitespace(const std::string &line) const
{
    return (line.find_first_not_of(' ') == line.npos);
}

bool Parser::validLine(const std::string &line) const
{
    std::string temp = line;
    temp.erase(0, temp.find_first_not_of("\t "));
    if (temp[0] == '=')
        return false;

    for (size_t i = temp.find('=') + 1; i < temp.length(); i++)
        if (temp[i] != ' ')
            return true;

    return false;
}

void Parser::extractKey(std::string &key, size_t const &sepPos, const std::string &line) const
{
    key = line.substr(0, sepPos);
    if (key.find('\t') != line.npos || key.find(' ') != line.npos)
        key.erase(key.find_first_of("\t "));
}

void Parser::extractValue(std::string &value, size_t const &sepPos, const std::string &line) const
{
    value = line.substr(sepPos + 1);
    value.erase(0, value.find_first_not_of("\t "));
    value.erase(value.find_last_not_of("\t ") + 1);
}

void Parser::extractContents(const std::string &line)
{
    std::string temp = line;
    temp.erase(0, temp.find_first_not_of("\t "));
    size_t sepPos = temp.find('=');

    std::string key, value;
    extractKey(key, sepPos, temp);
    extractValue(value, sepPos, temp);

    if (!keyExists(key))
        if(isNumber(value))
            contents.insert(std::pair<std::string, std::string > (key, value));
        else
            contents.insert(std::pair<std::string, std::string > (key, 0)); //set default(0) value
    else
        std::cout << "CFG: Can only have unique key names!\n" << std::endl;
}

void Parser::parseLine(const std::string &line, size_t const lineNo)
{
    if (line.find('=') == line.npos)
        std::cout << "CFG: Couldn't find separator on line: " << T_to_string(lineNo) << "\n" << std::endl;

    if (!validLine(line))
        std::cout << "CFG: Bad format for line: " << T_to_string(lineNo) << "\n" << std::endl;

    extractContents(line);
}

void Parser::ExtractKeys()
{
    std::ifstream file;
    file.open(fName.c_str());
    if (!file)
        std::cout << "CFG: File " << fName << " couldn't be found!\n" << std::endl;

    std::string line;
    size_t lineNo = 0;
    while (std::getline(file, line))
    {
        lineNo++;
        std::string temp = line;

        if (temp.empty() || temp == "\n" || temp == "\r")
            continue;

        removeComment(temp);
        if (onlyWhitespace(temp))
            continue;

        parseLine(temp, lineNo);
    }

    file.close();
}

Parser::Parser(const std::string &fName)
{
    this->fName = fName;
    ExtractKeys();
}

bool Parser::keyExists(const std::string &key) const
{
    return contents.find(key) != contents.end();
}

bool Parser::isValidFile() const
{
    bool resultValidation = true;

    for (Map::const_iterator i = m.begin(); i != m.end(); ++i){
        std::string exprValue = i->second.second;
        int typeValue = i->second.first;

        //Validate Expresion Range
        if((exprValue.find_first_of('[') == 0) &&
           ((exprValue.find_first_of(']') + 1) == exprValue.length() || (exprValue.find_first_of(')') + 1) == exprValue.length()) ){

            int pos = exprValue.find('-');
            std::string num_from = exprValue.substr(1, pos - 1);
            std::string num_to = exprValue.substr(pos + 1, exprValue.length() - pos - 2);

            bool evaluation = validateRange(typeValue, i->first, num_from, num_to);
            resultValidation = resultValidation && evaluation;

            if(!evaluation){
                std::cout << "Validation Error: The value of the key " << i->first << " is invalid. Expression expected: " << exprValue << std::endl;
            }
        } else if((count(exprValue.begin(), exprValue.end(), '(') +
                         count(exprValue.begin(), exprValue.end(), ')')) == 2) {

        }
    }

    return resultValidation;
}

